﻿using System.Text;

namespace Luka;

/// <summary>
/// 文件操作
/// </summary>
public static class FileExtensions
{
    /// <summary>
    /// 检测指定目录是否存在，如果存在则返回true。
    /// </summary>
    /// <param name="directoryPath">目录的绝对路径</param>
    public static bool IsExistDirectory(this string directoryPath) => Directory.Exists(directoryPath);

    /// <summary>
    /// 检测指定文件是否存在，如果存在则返回true。
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static bool IsExistFile(this string filePath) => File.Exists(filePath);

    /// <summary>
    /// 检测指定目录是否为空
    /// </summary>
    /// <param name="directoryPath">指定目录的绝对路径</param>
    public static bool IsEmptyDirectory(this string directoryPath)
    {
        //判断是否存在文件
        string[] fileNames = GetFileNames(directoryPath);
        if (fileNames.Length > 0)
            return false;
        //判断是否存在文件夹
        string[] directoryNames = GetDirectories(directoryPath);
        if (directoryNames.Length > 0)
            return false;
        return true;
    }

    /// <summary>
    /// 检测指定目录中是否存在指定的文件,若要搜索子目录请使用重载方法.
    /// </summary>
    /// <param name="directoryPath">指定目录的绝对路径</param>
    /// <param name="searchPattern">模式字符串，"*"代表0或N个字符，"?"代表1个字符。
    /// 范例："Log*.xml"表示搜索所有以Log开头的Xml文件。</param>
    public static bool IsContainsFile(this string directoryPath, string searchPattern) => FileExtensions.GetFileNames(directoryPath, searchPattern, false).Length > 0;

    /// <summary>
    /// 创建一个目录
    /// </summary>
    /// <param name="directoryPath">目录的绝对路径</param>
    public static void CreateDirectory(this string directoryPath)
    {
        //如果目录不存在则创建该目录
        if (!IsExistDirectory(directoryPath)) Directory.CreateDirectory(directoryPath);
    }

    /// <summary>
    /// 创建一个文件
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static void CreateFile(this string filePath)
    {
        //如果文件不存在则创建该文件
        if (!FileExtensions.IsExistFile(filePath))
        {
            //创建一个FileInfo对象
            FileInfo file = new(filePath);
            //创建文件
            FileStream fs = file.Create();
            //关闭文件流
            fs.Close();
        }
    }

    /// <summary>
    /// 创建一个文件,并将字节流写入文件。
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    /// <param name="buffer">二进制流数据</param>
    public static void CreateFile(this string filePath, byte[] buffer)
    {
        //如果文件不存在则创建该文件
        if (!IsExistFile(filePath))
        {
            //创建一个FileInfo对象
            FileInfo file = new(filePath);
            //创建文件
            FileStream fs = file.Create();
            //关闭文件流
            fs.Close();
        }
    }

    /// <summary>
    /// 获取文本文件的行数
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static int GetLineCount(this string filePath) => File.ReadAllLines(filePath).Length;

    /// <summary>
    /// 获取一个文件的长度,单位为Byte
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static int GetFileSize(this string filePath) => (int)(new FileInfo(filePath)).Length;

    /// <summary>
    /// 获取指定目录中所有文件列表
    /// </summary>
    /// <param name="directoryPath">指定目录的绝对路径</param>
    public static string[] GetFileNames(this string directoryPath)
    {
        //如果目录不存在，则抛出异常
        if (!IsExistDirectory(directoryPath))
            throw new FileNotFoundException();
        //获取文件列表
        return Directory.GetFiles(directoryPath);
    }

    /// <summary>
    /// 获取指定目录及子目录中所有文件列表
    /// </summary>
    /// <param name="directoryPath">指定目录的绝对路径</param>
    /// <param name="searchPattern">模式字符串，"*"代表0或N个字符，"?"代表1个字符。
    /// 范例："Log*.xml"表示搜索所有以Log开头的Xml文件。</param>
    /// <param name="isSearchChild">是否搜索子目录</param>
    public static string[] GetFileNames(this string directoryPath, string searchPattern, bool isSearchChild) => !IsExistDirectory(directoryPath) ? throw new FileNotFoundException() : isSearchChild ? Directory.GetFiles(directoryPath, searchPattern, SearchOption.AllDirectories) : Directory.GetDirectories(directoryPath);

    /// <summary>
    /// 获取指定目录中所有子目录列表,若要搜索嵌套的子目录列表,请使用重载方法.
    /// </summary>
    /// <param name="directoryPath">指定目录的绝对路径</param>
    public static string[] GetDirectories(this string directoryPath) => Directory.GetDirectories(directoryPath);

    /// <summary>
    /// 获取指定目录及子目录中所有子目录列表
    /// </summary>
    /// <param name="directoryPath">指定目录的绝对路径</param>
    /// <param name="searchPattern">模式字符串，"*"代表0或N个字符，"?"代表1个字符。
    /// 范例："Log*.xml"表示搜索所有以Log开头的Xml文件。</param>
    /// <param name="isSearchChild">是否搜索子目录</param>
    public static string[] GetDirectories(string directoryPath, string searchPattern, bool isSearchChild) => isSearchChild ? Directory.GetDirectories(directoryPath, searchPattern, SearchOption.AllDirectories) : Directory.GetDirectories(directoryPath, searchPattern, SearchOption.TopDirectoryOnly);

    /// <summary>
    /// 向文本文件中写入内容
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    /// <param name="content">写入的内容</param>
    public static void WriteText(this string filePath, string content) => File.WriteAllText(filePath, content);

    /// <summary>
    /// 向文本文件的尾部追加内容
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    /// <param name="content">写入的内容</param>
    public static void AppendText(this string filePath, string content) => File.AppendAllText(filePath, content);

    /// <summary>
    /// 将源文件的内容复制到目标文件中
    /// </summary>
    /// <param name="sourceFilePath">源文件的绝对路径</param>
    /// <param name="destFilePath">目标文件的绝对路径</param>
    public static void Copy(this string sourceFilePath, string destFilePath) => File.Copy(sourceFilePath, destFilePath, true);

    /// <summary>
    /// 将文件移动到指定目录
    /// </summary>
    /// <param name="sourceFilePath">需要移动的源文件的绝对路径</param>
    /// <param name="descDirectoryPath">移动到的目录的绝对路径</param>
    public static void Move(this string sourceFilePath, string descDirectoryPath)
    {
        //获取源文件的名称
        string sourceFileName = GetFileName(sourceFilePath);
        if (IsExistDirectory(descDirectoryPath))
        {
            //如果目标中存在同名文件,则删除
            if (FileExtensions.IsExistFile(descDirectoryPath + "\\" + sourceFileName))
                DeleteFile(descDirectoryPath + "\\" + sourceFileName);
            //将文件移动到指定目录
            File.Move(sourceFilePath, descDirectoryPath + "\\" + sourceFileName);
        }
    }

    /// <summary>
    /// 从文件的绝对路径中获取文件名( 包含扩展名 )
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static string GetFileName(this string filePath) => new FileInfo(filePath).Name;

    /// <summary>
    /// 从文件的绝对路径中获取文件名( 不包含扩展名 )
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static string GetFileNameNoExtension(this string filePath) => new FileInfo(filePath).Name.Split('.')[0];

    /// <summary>
    /// 从文件的绝对路径中获取扩展名
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static string GetExtension(this string filePath) => new FileInfo(filePath).Extension;

    /// <summary>
    /// 将文件读取到缓冲区中
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static byte[] FileToBytes(this string filePath)
    {
        //获取文件的大小
        int fileSize = GetFileSize(filePath);
        //创建一个临时缓冲区
        byte[] buffer = new byte[fileSize];
        //创建一个文件流
        FileInfo fi = new(filePath);
        FileStream fs = fi.Open(FileMode.Open);
        try
        {
            //将文件流读入缓冲区
            fs.Read(buffer, 0, fileSize);
            return buffer;
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            //关闭文件流
            fs.Close();
        }
    }

    /// <summary>
    /// 将文件读取到字符串中
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    /// <param name="encoding">字符编码</param>
    public static string FileToString(this string filePath, Encoding encoding)
    {
        //创建流读取器
        StreamReader reader = new(filePath, encoding);
        try
        {
            //读取流
            return reader.ReadToEnd();
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            //关闭流读取器
            reader.Close();
        }
    }

    /// <summary>
    /// 清空指定目录下所有文件及子目录,但该目录依然保存.
    /// </summary>
    /// <param name="directoryPath">指定目录的绝对路径</param>
    public static void ClearDirectory(this string directoryPath)
    {
        if (IsExistDirectory(directoryPath))
        {
            //删除目录中所有的文件
            string[] fileNames = GetFileNames(directoryPath);
            for (int i = 0; i < fileNames.Length; i++)
            {
                DeleteFile(fileNames[i]);
            }
            //删除目录中所有的子目录
            string[] directoryNames = GetDirectories(directoryPath);
            for (int i = 0; i < directoryNames.Length; i++)
            {
                DeleteDirectory(directoryNames[i]);
            }
        }
    }

    /// <summary>
    /// 清空文件内容
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static void ClearFile(this string filePath)
    {
        //删除文件
        File.Delete(filePath);
        //重新创建该文件
        CreateFile(filePath);
    }

    /// <summary>
    /// 删除指定文件
    /// </summary>
    /// <param name="filePath">文件的绝对路径</param>
    public static void DeleteFile(this string filePath)
    {
        if (IsExistFile(filePath))
            File.Delete(filePath);
    }

    /// <summary>
    /// 删除指定目录及其所有子目录
    /// </summary>
    /// <param name="directoryPath">指定目录的绝对路径</param>
    public static void DeleteDirectory(this string directoryPath)
    {
        if (IsExistDirectory(directoryPath))
            Directory.Delete(directoryPath, true);
    }

    /// <summary>
    /// 将字节(B)转换成兆(M)，并保留两位小数
    /// </summary>
    public static string ByteToMB(int byteLength) => Math.Round((double)byteLength / (1024 * 1024), 2).ToString() + "M";
}